package com.lzj.demo.stream;

import cn.hutool.json.JSONUtil;
import com.lzj.demo.dto.Student;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Comparator.comparing;
import static java.util.Comparator.reverseOrder;

@Slf4j
public class LambdaTest {
    private static final List<Student> studentList = new ArrayList<>(2);

    //初始化数据
    static {
        studentList.add(new Student(1L, "马超", 100));
        studentList.add(new Student(2L, "吕布", 200));
    }

    //java8自带的常用函数式接口
    public static void main(String[] args) {
        Student student = new Student(1L,"铠", 10);
        //断言:判断真假
        System.out.println("===演示Predicate===");
        Predicate<Student> predicate = x -> x.getId() > 1L;
        System.out.println("当前对象的id>1吗？：" + predicate.test(student));
        //将T映射为R（转换功能）
        System.out.println("===演示Function===");
        Function<Student, String> function = Student::getName;
        String name = function.apply(student);
        System.out.println(name);
        //生产消息
        System.out.println("===演示Supplier===");
        Supplier<Integer> supplier = () -> Integer.valueOf(BigDecimal.TEN.toString());
        System.out.println(supplier.get());
        //消费消息
        System.out.println("===演示Consumer===");
        Consumer<String> consumer = System.out::println;
        consumer.accept("演示Consumer");
        //一元操作
        System.out.println("===演示一元操作===");
        UnaryOperator<Boolean> unaryOperator = uglily -> !uglily;
        Boolean apply2 = unaryOperator.apply(true);
        System.out.println(apply2);
        //二元操作
        System.out.println("===演示二元操作===");
        BinaryOperator<Integer> operator = (x, y) -> x * y;
        Integer integer = operator.apply(2, 3);
        System.out.println(integer);
        //自定义函数式接口
        System.out.println("===演示自定义函数式接口===");
        selfDefinition(() -> "我是一个演示的函数式接口");
    }

    @Test
    public void consumer(){
        Consumer<String> consumer = LambdaTest::printLogInfo;
        consumer.accept("日志内容");
    }

    private static void printLogInfo(String info){
        log.info("输出日志:{}",info);
    }

    /**
     * 演示自定义函数式接口使用
     *
     * @param worker
     */
    public static void selfDefinition(Worker worker) {
        String work = worker.work();
        System.out.println(work);
    }

    //【collect】内部就是【Supplier】接口
    @Test
    public void collect(){
        List<Student> studentList = Stream.of(
                new Student(1L, "马超", 100),
                new Student(2L, "吕布", 200)
        ).collect(Collectors.toList());
        log.info("studentList:{}", JSONUtil.toJsonStr(studentList));
    }

    //【filter】内部就是【Predicate】接口
    @Test
    public void filter(){
        List<Student> list = studentList.stream()
                .filter(stu -> stu.getAge() > 150)
                .collect(Collectors.toList());
        log.info("studentList:{}", JSONUtil.toJsonStr(list));
    }

    //【map】内部就是【Function】接口
    @Test
    public void function(){
        List<String> names = studentList.stream().map(student -> student.getName())
                .collect(Collectors.toList());
        System.out.println(names);
    }

    //【reduce】内部就是【BinaryOperator】接口
    //【iterate】内部就是【UnaryOperator】接口
    @Test
    public void reduce(){
        Integer result = studentList.stream().map(Student::getAge).reduce(0, (addResult, x) -> addResult + x);
        //Integer result = studentList.stream().map(Student::getAge).reduce(0, Integer::sum);
        System.out.println(result);
    }

    //【peek,forEach】内部就是【Consumer】接口
    @Test
    public void peek(){
        studentList.stream().filter(x->x.getId() > 1L).peek(x->{
            log.info("x:{}",JSONUtil.toJsonStr(x));
        });
    }
    @Test
    public void forEach(){
        studentList.stream().filter(x->x.getId() > 1L).forEach(x->{
            log.info("x:{}",JSONUtil.toJsonStr(x));
        });
    }

    //【flatMap】内部就是【Function】接口
    @Test
    public void flatMap(){
        List<Student> AList = new ArrayList<>(3);
        AList.add(new Student(1L,"路飞", 22));
        AList.add(new Student(2L,"红发", 40));
        AList.add(new Student(3L,"白胡子", 50));

        List<Student> BList =  Arrays.asList(
                new Student(5L,"艾斯", 25)
                ,new Student(6L,"雷利", 48)
        );

        //使用【过滤流】替换【if-else】
        List<Student> filterList = AList.stream().filter(x -> x.getId() > 1L).collect(Collectors.toList());

        //使用【流合并】优化【多层循环遍历】
        List<Student> studentList = Stream.of(AList,BList)
                //将多个流合并为一个流
                .flatMap(Collection::stream)
                //将流转换为list
                .collect(Collectors.toList());

        System.out.println(studentList);
    }

    @Test
    public void collector(){
        OutstandingClass outstandingClass = biggestGroup();
        System.out.println("人数最多的班级是：" + outstandingClass.getName());
        System.out.println("一班平均年龄是：" + averageAge());
    }

    /**
     * 获取人数最多的班级
     */
    private static OutstandingClass biggestGroup() {
        OutstandingClass ostClass1 = new OutstandingClass("一班", studentList);
        List<Student> studentList2 = new ArrayList<>(studentList);
        studentList2.add(new Student(3L,"铠",300));
        OutstandingClass ostClass2 = new OutstandingClass("二班", studentList2);
        List<OutstandingClass> sortedList = Stream.of(ostClass1, ostClass2).sorted(Comparator.comparing(ostClass -> ostClass.getStudents().size(),reverseOrder())).collect(Collectors.toList());
        return sortedList.get(0);
    }

    /**
     * 计算平均年龄
     */
    private static Double averageAge() {
        OptionalDouble optionalDouble = studentList.stream().mapToInt(Student::getAge).average();
        if(optionalDouble.isPresent()){
            return optionalDouble.getAsDouble();
        }
        return null;
    }

    /**
     * 数据分片
     * */
    @Test
    public void partition(){
        Map<Boolean, List<Student>> partitionMap = studentList.stream().collect(Collectors.partitioningBy(student -> student.getId() > 1L));
        log.info("ageMap:{}",JSONUtil.toJsonStr(partitionMap));
    }

    /**
     * 数据分组
     * */
    @Test
    public void group(){
        //将学生根据年龄分组
        Map<Integer,List<Student>> ageMap = studentList.stream().collect(Collectors.groupingBy(Student::getAge));
        log.info("ageMap:{}",JSONUtil.toJsonStr(ageMap));
    }

    /**
     * 字符串拼接
     * */
    @Test
    public void joining(){
        String names = studentList.stream().map(Student::getName).collect(Collectors.joining(",","[","]"));
        System.out.println(names);
    }
}
